#include "Semantico.h"

void copiaArquivo(char arquivo[50], FILE* saida) {

    FILE* arq;
    arq = fopen(arquivo, "rt");

    char linha[200];

    while((fgets(linha, 200, arq)) != NULL)
        fprintf(saida, "%s", linha);

}

void montaFimPrograma(FILE* saida, int *IC) {

    fprintf(saida,"\n\tIL_%04x:  nop", *IC);
    (*IC)++;
    fprintf(saida,"\n\tIL_%04x:  ldloc.0", *IC);
    (*IC)++;
    fprintf(saida,"\n\tIL_%04x:  ldloc.0", *IC);
    (*IC)++;
    fprintf(saida,"\n\tIL_%04x:  callvirt\tinstance void Kipple.KippleLib::PrintOutput(class Kipple.KippleLib)", *IC);
    *IC = *IC + 5;
    fprintf(saida,"\n\tIL_%04x:  nop", *IC);
    (*IC)++;
    fprintf(saida,"\n\tIL_%04x:  ret", *IC);

}

int semantico_num(FILE* saida, int atualState, int proximoState, char atualNome[10], int value, Stack** pilha, int *IC){

    int ok = TRUE;

    if(strcmp(atualNome, "exp") == 0 && atualState == 0 && proximoState == 2){
        trata_num(saida, pilha, value, IC);
    }

    return ok;
}

int semantico_let(FILE* saida, int atualState, int proximoState, char atualNome[10], int id, Stack** pilha, LinkedTable* symbolTable, int *flagWhile, int *IC) {

    int ok = TRUE;

    if(strcmp(atualNome, "lexp") == 0 && atualState == 0 && proximoState == 1){
        trata_let(saida, pilha, id, symbolTable, flagWhile, IC);
    }

    return ok;
}

int semantico_rwd(FILE* saida, int atualState, int proximoState, char atualNome[10], char rwd[100], Stack** pilha, int *rotulos, int *flagWhile, int *IC){

    int ok = TRUE;
    if(strcmp(atualNome, "exp") == 0 && atualState == 2 && proximoState == 3){
        empilha_operador(pilha, rwd[1]);
    }
    if(strcmp(atualNome, "lexp") == 0 && atualState == 1 && (proximoState == 3 || proximoState == 4)){
        empilha_operador(pilha, rwd[1]);
    }
    if(strcmp(atualNome, "lexp") == 0 && atualState == 1 && proximoState == 2){
        clear(saida, pilha, IC);
    }
    if(strcmp(atualNome, "comandos") == 0 && atualState == 0 && proximoState == 2){
        ini_while(pilha, flagWhile, rotulos);
    }
    if(strcmp(atualNome, "comandos") == 0 && atualState == 3 && proximoState == 1){
        fim_while(saida, pilha, IC);
    }
    return ok;
}

void trata_num(FILE* saida, Stack** pilha, int value, int *IC){

    if(topoId(*pilha) == '>' || topoId(*pilha) == '<' || topoId(*pilha) == '+' || topoId(*pilha) == '-'){
        int opndo1;
        int opdor;
        desempilha(pilha, &opdor);
        desempilha(pilha, &opndo1);

        resolveOp2Int(saida, opndo1, value, opdor, IC);

    } else if(topo(*pilha) == '@') {
        int aux;
        desempilha(pilha, &aux);
    }  else if(topo(*pilha) == '#') {
        int aux;
        desempilha(pilha, &aux);
    }

    empilha(pilha, '#', value);

}

void trata_let(FILE* saida, Stack** pilha, int id, LinkedTable* symbolTable, int *flagWhile, int *IC){

    char lido[100];

    strcpy(lido, "");
    buscaPorId(lido, id, symbolTable, FALSE);

    if(*flagWhile == TRUE){

        fprintf(saida, "\n\tL_INIW%d:", topoId(*pilha));
        fprintf(saida, "\n\tIL_%04x:  nop", *IC);
        (*IC)++;
        fprintf(saida, "\n\tIL_%04x:  ldloc.0", *IC);
        (*IC)++;
        fprintf(saida, "\n\tIL_%04x:  ldstr\t\t\"%c\"", *IC, lido[0]);
        *IC = *IC + 5;
        fprintf(saida, "\n\tIL_%04x:  callvirt\tinstance bool Kipple.KippleLib::Empty(string)", *IC);
        *IC = *IC + 5;
        fprintf(saida, "\n\tIL_%04x:  ldc.i4.0", *IC);
        (*IC)++;
        fprintf(saida, "\n\tIL_%04x:  ceq", *IC);
        *IC = *IC + 2;
        fprintf(saida, "\n\tIL_%04x:  stloc.1", *IC);
        (*IC)++;
        fprintf(saida, "\n\tIL_%04x:  ldloc.1", *IC);
        (*IC)++;
        fprintf(saida, "\n\tIL_%04x:  brfalse\tL_FIMW%d", *IC, topoId(*pilha));
        *IC = *IC + 2;

        *flagWhile = FALSE;
    }

    if(topoId(*pilha) == '>' || topoId(*pilha) == '<' || topoId(*pilha) == '+' || topoId(*pilha) == '-'){
        int opndo1;
        int opdor;
        int tipo;
        desempilha(pilha, &opdor);
        tipo = desempilha(pilha, &opndo1);

        if(tipo == '#'){
            resolveOp1Int(saida, opndo1, lido[0], opdor, IC);
        }

        if(tipo == '@'){
            resolveOpId(saida, opndo1, lido[0], opdor, IC);
        }

    } else if(topo(*pilha) == '@') {
        int aux;
        desempilha(pilha, &aux);
    } else if(topo(*pilha) == '#') {
        int aux;
        desempilha(pilha, &aux);
    }

    empilha(pilha, '@', lido[0]);
}


void ini_while(Stack** pilha, int *flagWhile, int *rotulos){
    *flagWhile = TRUE;
    empilha(pilha, '$', *rotulos);
    (*rotulos)++;
}


void fim_while(FILE* saida, Stack** pilha, int *IC){
    int rot;
    if(topo(*pilha) == '@' || topo(*pilha) == '#')
        desempilha(pilha, &rot);
    desempilha(pilha, &rot);

    fprintf(saida, "\n\tIL_%04x:  br\tL_INIW%d", *IC, rot);
    *IC = *IC + 2;
    fprintf(saida, "\n\tL_FIMW%d:", rot);
}


void empilha_operador(Stack** pilha, char op){
    empilha(pilha, '&', op);
}

void clear(FILE* saida, Stack** pilha, int *IC){
    int opndo1;
    desempilha(pilha, &opndo1);
    fprintf(saida, "\n\tIL_%04x:  nop", *IC);
    (*IC)++;
    fprintf(saida, "\n\tIL_%04x:  ldloc.0", *IC);
    (*IC)++;
    fprintf(saida, "\n\tIL_%04x:  ldstr\t\t\"%c\"", *IC, opndo1);
    *IC = *IC + 5;
    fprintf(saida, "\n\tIL_%04x:  callvirt\tinstance void Kipple.KippleLib::Clear(string)", *IC);
    *IC = *IC + 5;
    fprintf(saida, "\n");


}

void resolveOpId(FILE* saida, char opndo1, char opndo2, char opdor, int *IC){
    if(opdor == '>'){
        fprintf(saida, "\n\tIL_%04x:  nop", *IC);
        (*IC)++;
        fprintf(saida, "\n\tIL_%04x:  ldloc.0", *IC);
        (*IC)++;
        fprintf(saida, "\n\tIL_%04x:  ldstr\t\t\"%c\"", *IC, opndo1);
        *IC = *IC + 5;
        fprintf(saida, "\n\tIL_%04x:  ldstr\t\t\"%c\"", *IC, opndo2);
        *IC = *IC + 5;
        fprintf(saida, "\n\tIL_%04x:  callvirt\tinstance void Kipple.KippleLib::PushId(string, string)", *IC, opndo2);
        *IC = *IC + 5;
        fprintf(saida, "\n");
    } else if(opdor == '<'){
        fprintf(saida, "\n\tIL_%04x:  nop", *IC);
        (*IC)++;
        fprintf(saida, "\n\tIL_%04x:  ldloc.0", *IC);
        (*IC)++;
        fprintf(saida, "\n\tIL_%04x:  ldstr\t\t\"%c\"", *IC, opndo2);
        *IC = *IC + 5;
        fprintf(saida, "\n\tIL_%04x:  ldstr\t\t\"%c\"", *IC, opndo1);
        *IC = *IC + 5;
        fprintf(saida, "\n\tIL_%04x:  callvirt\tinstance void Kipple.KippleLib::PushId(string, string)", *IC, opndo2);
        *IC = *IC + 5;
        fprintf(saida, "\n");
    } else if(opdor == '+' || opdor == '-'){
        fprintf(saida, "\n\tIL_%04x:  nop", *IC);
        (*IC)++;
        fprintf(saida, "\n\tIL_%04x:  ldloc.0", *IC);
        (*IC)++;
        fprintf(saida, "\n\tIL_%04x:  ldstr\t\t\"%c\"", *IC, opndo1);
        *IC = *IC + 5;
        fprintf(saida, "\n\tIL_%04x:  ldstr\t\t\"%c\"", *IC, opndo2);
        *IC = *IC + 5;
        fprintf(saida, "\n\tIL_%04x:  ldstr\t\t\"%c\"", *IC, opdor);
        *IC = *IC + 5;
        fprintf(saida, "\n\tIL_%04x:  callvirt\tinstance void Kipple.KippleLib::OperateId(string, string, string)", *IC, opndo2);
        *IC = *IC + 5;
        fprintf(saida, "\n");
    }

}

void resolveOp1Int(FILE* saida, int opndo1, char opndo2, char opdor, int *IC){

    if(opdor == '>'){
        fprintf(saida, "\n\tIL_%04x:  nop", *IC);
        (*IC)++;
        fprintf(saida, "\n\tIL_%04x:  ldloc.0", *IC);
        (*IC)++;
        fprintf(saida, "\n\tIL_%04x:  ldc.i4\t0x%x", *IC, opndo1);
        *IC = *IC + 5;
        fprintf(saida, "\n\tIL_%04x:  ldstr\t\t\"%c\"", *IC, opndo2);
        *IC = *IC + 5;
        fprintf(saida, "\n\tIL_%04x:  callvirt\tinstance void Kipple.KippleLib::PushInt(int32, string)", *IC, opndo2);
        *IC = *IC + 5;
        fprintf(saida, "\n");
    }

}

void resolveOp2Int(FILE* saida, char opndo1, int opndo2, char opdor, int *IC){

    if(opdor == '<'){
        fprintf(saida, "\n\tIL_%04x:  nop", *IC);
        (*IC)++;
        fprintf(saida, "\n\tIL_%04x:  ldloc.0", *IC);
        (*IC)++;
        fprintf(saida, "\n\tIL_%04x:  ldc.i4\t0x%x", *IC, opndo2);
        *IC = *IC + 5;
        fprintf(saida, "\n\tIL_%04x:  ldstr\t\t\"%c\"", *IC, opndo1);
        *IC = *IC + 5;
        fprintf(saida, "\n\tIL_%04x:  callvirt\tinstance void Kipple.KippleLib::PushInt(int32, string)", *IC, opndo2);
        *IC = *IC + 5;
        fprintf(saida, "\n");
    } else if(opdor == '+' || opdor == '-'){
        fprintf(saida, "\n\tIL_%04x:  nop", *IC);
        (*IC)++;
        fprintf(saida, "\n\tIL_%04x:  ldloc.0", *IC);
        (*IC)++;
        fprintf(saida, "\n\tIL_%04x:  ldstr\t\t\"%c\"", *IC, opndo1);
        *IC = *IC + 5;
        fprintf(saida, "\n\tIL_%04x:  ldc.i4\t0x%x", *IC, opndo2);
        *IC = *IC + 5;
        fprintf(saida, "\n\tIL_%04x:  ldstr\t\t\"%c\"", *IC, opdor);
        *IC = *IC + 5;
        fprintf(saida, "\n\tIL_%04x:  callvirt\tinstance void Kipple.KippleLib::OperateInt(string, int32, string)", *IC, opndo2);
        *IC = *IC + 5;
        fprintf(saida, "\n");
    }

}
